title | description | sidebar_label |
---|---|---|
Creating a Supabase client for SSR |
Configure Supabase client to use Cookies |
Creating a client |
The ssr
package configures Supabase to use Cookies, which is required for server-side languages and frameworks.
npm install @supabase/ssr @supabase/supabase-js
Create a .env.local
file in your project root directory. You can get your SUPABASE_URL
and SUPABASE_ANON_KEY
from inside your Supabase project's dashboard.
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
Create a .env.local
file in your project root directory. You can get your SUPABASE_URL
and SUPABASE_ANON_KEY
from inside your Supabase project's dashboard.
PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
Create a .env
file in your project root directory. You can get your SUPABASE_URL
and SUPABASE_ANON_KEY
from inside your Supabase project's dashboard.
PUBLIC_SUPABASE_URL=your_supabase_project_url
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
Create a .env
file in your project root directory. You can get your SUPABASE_URL
and SUPABASE_ANON_KEY
from inside your Supabase project's dashboard.
SUPABASE_URL=your_supabase_project_url
SUPABASE_ANON_KEY=your_supabase_anon_key
Create a .env
file in your project root directory. You can get your SUPABASE_URL
and SUPABASE_ANON_KEY
from inside your Supabase project's dashboard.
SUPABASE_URL=your_supabase_project_url
SUPABASE_ANON_KEY=your_supabase_anon_key
Install dotenv
npm i dotenv
and initialize it
const express = require("express")
const dotenv = require("dotenv")
dotenv.config()
...
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework"
Creating a Supabase client with the ssr
package automatically configures it to use cookies. This means your user's session is available throughout the entire Next.js stack - client, server, App Router, Pages Router. It just works!
For the client utility functions, it is recommended to create them in a separate /utils/supabase
folder. That way, you can import them as client
into any component or page that needs access to Supabase. For further information, you can look at the Next.js Auth Guide.
<Tabs scrollable size="small" type="underlined" defaultActiveId="client-component" queryGroup="environment"
"use client";
import { createBrowserClient } from '@supabase/ssr'
export default function Page () {
const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
return ...
}
Server Components only have read access to cookies. Check the Middleware
tab for an example of refreshing expired sessions before loading a Server Component route.
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
export default async function Page () {
const cookieStore = cookies()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value
},
},
}
)
return ...
}
Server Components only have read access to cookies. This Middleware
example can be used to refresh expired sessions before loading Server Component routes.
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
export async function middleware(request: NextRequest) {
let response = NextResponse.next({
request: {
headers: request.headers,
},
})
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value
},
set(name: string, value: string, options: CookieOptions) {
request.cookies.set({
name,
value,
...options,
})
response = NextResponse.next({
request: {
headers: request.headers,
},
})
response.cookies.set({
name,
value,
...options,
})
},
remove(name: string, options: CookieOptions) {
request.cookies.set({
name,
value: '',
...options,
})
response = NextResponse.next({
request: {
headers: request.headers,
},
})
response.cookies.set({
name,
value: '',
...options,
})
},
},
}
)
await supabase.auth.getUser()
return response
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function POST(request: Request) {
const cookieStore = cookies()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value
},
set(name: string, value: string, options: CookieOptions) {
cookieStore.set({ name, value, ...options })
},
remove(name: string, options: CookieOptions) {
cookieStore.set({ name, value: '', ...options })
},
},
}
)
return ...
}
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
export default async function Page () {
const serverAction = async () => {
'use server'
const cookieStore = cookies()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value
},
set(name: string, value: string, options: CookieOptions) {
cookieStore.set({ name, value, ...options })
},
remove(name: string, options: CookieOptions) {
cookieStore.set({ name, value: '', ...options })
},
},
}
)
}
return ...
}
import { type GetServerSidePropsContext } from 'next'
import { createServerClient, type CookieOptions, serialize } from '@supabase/ssr'
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return context.req.cookies[name];
},
set(name: string, value: string, options: CookieOptions) {
context.res.appendHeader(
"Set-Cookie",
serialize(name, value, options)
);
},
remove(name: string, options: CookieOptions) {
context.res.appendHeader("Set-Cookie", serialize(name, "", options));
},
},
}
)
return {...}
}
The getStaticProps
function gets executed at build time, rather than per request. At build time,
there is no user, session or cookies. Therefore, we can use the standard createClient
function
from supabase-js
.
import { createClient } from '@supabase/supabase-js'
export const getStaticProps = async () => {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
)
return {...}
}
import { createServerClient, type CookieOptions, serialize } from "@supabase/ssr"
import { type NextApiRequest, type NextApiResponse } from "next"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return req.cookies[name];
},
set(name: string, value: string, options: CookieOptions) {
res.setHeader("Set-Cookie", serialize(name, value, options));
},
remove(name: string, options: CookieOptions) {
res.setHeader("Set-Cookie", serialize(name, "", options));
},
},
}
)
res.send(...)
}
import { createBrowserClient } from "@supabase/ssr"
export default function Index () {
const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
return ...
}
Creating a Supabase client with the ssr
package automatically configures it to use Cookies. This means your user's session is available throughout the entire SvelteKit stack - page, layout, server, hooks.
<Tabs scrollable size="small" type="underlined" defaultActiveId="hooks" queryGroup="environment"
API routes, server layout load and form actions can now access the supabase client from the event
object due to this hook.
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createServerClient } from '@supabase/ssr'
import type { Handle } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
cookies: {
get: (key) => event.cookies.get(key),
/**
* Note: You have to add the `path` variable to the
* set and remove method due to sveltekit's cookie API
* requiring this to be set, setting the path to an empty string
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
*/
set: (key, value, options) => {
event.cookies.set(key, value, { ...options, path: '/' })
},
remove: (key, options) => {
event.cookies.delete(key, { ...options, path: '/' })
},
},
})
/**
* Unlike `supabase.auth.getSession()`, which returns the session _without_
* validating the JWT, this function also calls `getUser()` to validate the
* JWT before returning the session.
*/
event.locals.safeGetSession = async () => {
const {
data: { session },
} = await event.locals.supabase.auth.getSession()
if (!session) {
return { session: null, user: null }
}
const {
data: { user },
error,
} = await event.locals.supabase.auth.getUser()
if (error) {
// JWT validation has failed
return { session: null, user: null }
}
return { session, user }
}
return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === 'content-range' || name === 'x-supabase-api-version'
},
})
}
Page components can get access to the supabase client from the data
object due to this load function.
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import type { LayoutLoad } from './$types'
import { createBrowserClient, isBrowser, parse } from '@supabase/ssr'
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
depends('supabase:auth')
const supabase = createBrowserClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
global: {
fetch,
},
cookies: {
get(key) {
if (!isBrowser()) {
return JSON.stringify(data.session)
}
const cookie = parse(document.cookie)
return cookie[key]
},
},
})
/**
* It's fine to use `getSession` here, because on the client, `getSession` is
* safe, and on the server, it reads `session` from the `LayoutData`, which
* safely checked the session using `safeGetSession`.
*/
const {
data: { session },
} = await supabase.auth.getSession()
return { supabase, session }
}
import type { LayoutServerLoad } from './$types'
export const load: LayoutServerLoad = async ({ locals: { safeGetSession } }) => {
const { session, user } = await safeGetSession()
return {
session,
user,
}
}
<script>
export let data
let { supabase } = data
$: ({ supabase } = data)
...
</script>
....
import { redirect } from '@sveltejs/kit';
export const GET = async (event) => {
const { url, locals: { supabase } } = event
...
}
import type { Actions } from './$types'
export const actions: Actions = {
default: async (event) => {
const { request, url, locals: { supabase } } = event
const formData = await request.formData()
const email = formData.get('email') as string
const password = formData.get('password') as string
...
}
}
By default, Astro apps are static. This means the requests for data happen at build time, rather than when the user requests a page. At build time, there is no user, session or cookies. Therefore, we need to configure Astro for Server-side Rendering (SSR) if you want data to be fetched dynamically per request.
import { defineConfig } from 'astro/config'
export default defineConfig({
output: 'server',
})
<Tabs scrollable size="small" type="underlined" defaultActiveId="astro-server" queryGroup="environment"
---
import { createServerClient, type CookieOptions } from "@supabase/ssr";
const supabase = createServerClient(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get(key: string) {
return Astro.cookies.get(key)?.value;
},
set(key: string, value: string, options: CookieOptions) {
Astro.cookies.set(key, value, options);
},
remove(key: string, options) {
Astro.cookies.delete(key, options);
},
},
}
);
---
<script>
const supabase = createBrowserClient(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY
);
</script>
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import type { APIContext } from "astro";
export async function GET(context: APIContext) {
const supabase = createServerClient(
import.meta.env.PUBLIC_SUPABASE_URL,
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
get(key: string) {
return context.cookies.get(key)?.value;
},
set(key: string, value: string, options: CookieOptions) {
context.cookies.set(key, value, options);
},
remove(key: string, options) {
context.cookies.delete(key, options);
},
},
}
);
return ...
}
<Tabs scrollable size="small" type="underlined" defaultActiveId="remix-loader" queryGroup="environment"
import { type LoaderFunctionArgs } from '@remix-run/node'
import { createServerClient, parse, serialize } from '@supabase/ssr'
export async function loader({ request }: LoaderFunctionArgs) {
const cookies = parse(request.headers.get('Cookie') ?? '')
const headers = new Headers()
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
cookies: {
get(key) {
return cookies[key]
},
set(key, value, options) {
headers.append('Set-Cookie', serialize(key, value, options))
},
remove(key, options) {
headers.append('Set-Cookie', serialize(key, '', options))
},
},
})
return new Response('...', {
headers,
})
}
import { type ActionFunctionArgs } from '@remix-run/node'
import { createServerClient, parse, serialize } from '@supabase/ssr'
export async function action({ request }: ActionFunctionArgs) {
const cookies = parse(request.headers.get('Cookie') ?? '')
const headers = new Headers()
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
cookies: {
get(key) {
return cookies[key]
},
set(key, value, options) {
headers.append('Set-Cookie', serialize(key, value, options))
},
remove(key, options) {
headers.append('Set-Cookie', serialize(key, '', options))
},
},
})
return new Response('...', {
headers,
})
}
import { type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { createBrowserClient } from "@supabase/ssr";
export async function loader({}: LoaderFunctionArgs) {
return {
env: {
SUPABASE_URL: process.env.SUPABASE_URL!,
SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!,
},
};
}
export default function Index() {
const { env } = useLoaderData<typeof loader>();
const supabase = createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY);
return ...
}
<Tabs scrollable size="small" type="underlined" defaultActiveId="server-client" queryGroup="environment"
const { createServerClient } = require('@supabase/ssr')
exports.createClient = (context) => {
return createServerClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, {
cookies: {
get: (key) => {
const cookies = context.req.cookies
const cookie = cookies[key] ?? ''
return decodeURIComponent(cookie)
},
set: (key, value, options) => {
if (!context.res) return
context.res.cookie(key, encodeURIComponent(value), {
...options,
sameSite: 'Lax',
httpOnly: true,
})
},
remove: (key, options) => {
if (!context.res) return
context.res.cookie(key, '', { ...options, httpOnly: true })
},
},
})
}
const express = require("express")
const dotenv = require("dotenv")
const { createClient } = require("./lib/supabase")
const app = express()
app.post("/hello-world", async function (req, res, next) {
const { email, emailConfirm } = req.body
...
const supabase = createClient({ req, res })
})
- Implement Authentication using Email and Password
- Implement Authentication using OAuth
- Learn more about SSR